package mrpanyu.guitool.yunduan;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;

import org.apache.commons.codec.binary.Base64;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;

import mrpanyu.guitool.base.util.CommonUtils;

public class YunduanExportFrame extends JFrame {

	private static final long serialVersionUID = 1L;

	protected File file;
	protected int imgSize;
	protected int pageSize;

	protected byte[] data;
	protected int pageNo;
	protected int totalPages;

	protected StringBuilder keyBuf = new StringBuilder();

	protected JLabel label;

	public YunduanExportFrame(File file, int imgSize, int pageSize) {
		this.file = file;
		this.imgSize = imgSize;
		this.pageSize = pageSize;
		this.pageNo = 0;
		this.data = CommonUtils.readBytes(file);
		this.totalPages = data.length / pageSize;
		if (data.length % pageSize > 0) {
			this.totalPages++;
		}
		this.init();
	}

	private void init() {
		label = new JLabel();
		label.setPreferredSize(new Dimension(imgSize, imgSize));
		this.getContentPane().add(label, BorderLayout.CENTER);
		drawPage();
		this.pack();
		this.setLocationByPlatform(false);
		this.setLocation(0, 0);
		this.setResizable(false);
		this.setAlwaysOnTop(true);
		this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		this.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				onKeyPressed(e);
			}
		});
	}

	private void drawPage() {
		int currentPageSize = pageNo == totalPages - 1 ? data.length % pageSize : pageSize;
		byte[] pageData = new byte[currentPageSize];
		System.arraycopy(data, pageNo * pageSize, pageData, 0, pageData.length);
		while (true) {
			try {
				pageData = AESUtils.encrypt(Constants.AES_KEY, pageData);
			} catch (Exception e) {
				e.printStackTrace();
				continue;
			}
			String encoded = Base64.encodeBase64String(pageData);
			String content = pageNo + ":" + (pageNo == totalPages - 1 ? "*" : "") + ":" + encoded;
			MultiFormatWriter writer = new MultiFormatWriter();
			try {
				BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, imgSize, imgSize);
				BufferedImage img = MatrixToImageWriter.toBufferedImage(bitMatrix);
				
				// try decode, may throw exception
				BufferedImageLuminanceSource luminateSource = new BufferedImageLuminanceSource(img);
				MultiFormatReader reader = new MultiFormatReader();
				Map<DecodeHintType, Object> hints = new HashMap<>();
				hints.put(DecodeHintType.POSSIBLE_FORMATS, EnumSet.allOf(BarcodeFormat.class));
				hints.put(DecodeHintType.TRY_HARDER, true);
				hints.put(DecodeHintType.PURE_BARCODE, true);
				hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
				reader.setHints(hints);
				String decoded = reader.decode(new BinaryBitmap(new HybridBinarizer(luminateSource))).getText();
				if (!content.equals(decoded)) {
					throw new RuntimeException("Generated Image incorrect");
				}
				
				label.setIcon(new ImageIcon(img));
				this.setTitle("Page " + (pageNo + 1) + "/" + totalPages);
				break;
			} catch (Exception e) {
				e.printStackTrace();
				continue;
			}
		}
	}

	private void onKeyPressed(KeyEvent e) {
		char c = e.getKeyChar();
		System.out.println(c);
		if (c == '[') {
			if (pageNo > 0) {
				pageNo--;
			}
			EventQueue.invokeLater(this::drawPage);
		} else if (c == ']') {
			if (pageNo < totalPages - 1) {
				pageNo++;
			}
			EventQueue.invokeLater(this::drawPage);
		} else if (c == '=') {
			EventQueue.invokeLater(this::drawPage);
		} else if (c == '\\') {
			EventQueue.invokeLater(() -> {
				this.setVisible(false);
				this.dispose();
			});
		} else if ("0123456789".indexOf(c) >= 0) {
			keyBuf.append(c);
		} else if (c == ';') {
			if (keyBuf.length() > 0) {
				int pn = Integer.parseInt(keyBuf.toString(), 10);
				if (pn >= 0 && pn <= totalPages - 1) {
					pageNo = pn;
				}
				EventQueue.invokeLater(this::drawPage);
			}
			keyBuf = new StringBuilder();
		}
	}

}
